package spring.lottery.server.imp;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import spring.lottery.common.exception.ServerException;
import spring.lottery.common.util.RedisUtil;
import spring.lottery.common.util.SerializeUtil;
import spring.lottery.controller.param.*;
import spring.lottery.dao.dataObject.*;
import spring.lottery.dao.mapper.*;
import spring.lottery.server.ActivitiesServer;
import spring.lottery.server.dto.*;
import spring.lottery.server.dto.ActivityListDto;
import spring.lottery.server.enums.ActivityStatusEnum;
import spring.lottery.server.exceptionCode.ServerConstantCode;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Service
public class ActivitiesServerImp implements ActivitiesServer {

    private static final String CREATE_ACTIVITY = "CREATE_ACTIVITY";

    @Autowired
    RedisUtil redisUtil;
    @Autowired
    UserMapper userMapper;
    @Autowired
    PrizeMapper prizeMapper;
    @Autowired
    ActivityMapper activityMapper;
    @Autowired
    ActivityUserMapper activityUserMapper;
    @Autowired
    ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    WinningRecordMapper winningRecordMapper;

    /**
     * select prizeId imageUrl prizeName description price
     */
    @Override
    public PrizeDtoResponse prizePrizeFindList(PageParam param) {
        List<PrizeDO> prizeDOS = prizeMapper.selectPrizeList((param.getCurrentPage() - 1) * param.getPageSize(), param.getPageSize());

        if(prizeDOS == null || prizeDOS.isEmpty()) {
            return null;
        }

        List<PrizeDto> collect = prizeDOS.stream().map(prizeDO -> {
            PrizeDto prizeDto = new PrizeDto();
            prizeDto.setPrizeId(prizeDO.getId());
            prizeDto.setImageUrl(prizeDO.getImageUrl());
            prizeDto.setPrizeName(prizeDO.getName());
            prizeDto.setDescription(prizeDO.getDescription());
            prizeDto.setPrice(prizeDO.getPrice());
            return prizeDto;
        }).toList();

        PrizeDtoResponse ret = new PrizeDtoResponse();
        ret.setTotal(prizeMapper.selectCount());
        ret.setRecords(collect);

        return ret;
    }

    @Override
    public List<GetUserListDto> findUserList() {

        List<UserDO> allUsers = userMapper.getAllUsers();

        List<GetUserListDto> collect = allUsers.stream().map(user -> {
            GetUserListDto dto = new GetUserListDto();
            dto.setUserId(user.getId());
            dto.setUserName(user.getUserName());
            dto.setIdentity(user.getIdentity());
            return dto;
        }).collect(Collectors.toList());

        log.info("返回的用户列表: {}",collect.toString());

        return collect;
    }

    @Override
    public Integer create(CreatePrizeParam param) {
        if(param.getPrizeName().contains(" ")){
            throw new ServerException(ServerConstantCode.PRIZE_NAME_CONTAINS_SPACE);
        }
        PrizeDO prize = new PrizeDO(param.getPrizeName(), param.getDescription(), param.getPrice(), param.getImageUrl());
        return prizeMapper.insert(prize);
    }


    /**
     * Json
     * {"activityName":"tesy",
     * "description":"rr",
     * ----
     * "activityPrizeList": [{"prizeId":71,"prizeAmount":1,"prizeTiers":"SECOND_PRIZE"}
     * ,{"prizeId":70,"prizeAmount":1,"prizeTiers":"FIRST_PRIZE"}],
     * ----
     * "activityUserList":[{"userId":55,"userName":"hadage"},
     * {"userId":56,"userName":"hhh"},{"userId":57,"userName":"cool"}]}
     * ret
     * activityId
     */
    @Override
    @Transactional(rollbackFor = SQLException.class)
    public Long createActivity(CreateActivityParam param) {

        // 这里要验证下活动是否是合法的
        verifyParam(param);

        // 储存活动
        ActivityDto activity = new ActivityDto(null, param.getActivityName(), param.getDescription(), ActivityStatusEnum.RUNNING,true);

        activityMapper.insert(activity);

        //保存 activityPrizeList
        List<ActivityPrize> activityPrizeList = saveActivityPrize(activity,param.getActivityPrizeList());

        //保存 ActivityUserList
        List<ActivityUser> activityUserList = saveActivityUser(activity, param.getActivityUserList());

        /**
         * 还要查询这些奖品和人员的信息
         * 要的是 prize:  PrizeDto + prizeAmount prizeTiers;
         */

        //查询人员信息
        List<Long> userIds = activityUserList.stream().map(ActivityUser::getUserId).toList();

        List<UserDto> usInfS = userMapper.selectByIds(userIds);

        //查询奖品信息
        List<Long> prizeIds = activityPrizeList.stream().map(ActivityPrize::getPrizeId).toList();

        List<PrizeDO> prizeDOS = prizeMapper.selectByIds(prizeIds);

        //整合奖品信息PrizeDO  ActivityPrize
        List<ActivityPrizeInf> acInfS = convertToActivityPrizeInf(activityPrizeList, prizeDOS);

        /**
         * save to redis
         * 活动加奖品加人员
         */
        saveToRedis(activity, acInfS, usInfS);

        return activity.getId();
    }

    private void verifyParam(CreateActivityParam param) {
        //人员数量要大于等于奖品数量
        int users = param.getActivityUserList().size();

        int prizes = 0;

        for(ActivityPrize a: param.getActivityPrizeList()) {
            prizes += a.getPrizeAmount();
        }

        if (prizes > users) {
            throw new ServerException(ServerConstantCode.USERS_MORE_THAN_PRIZE,"prizes="+prizes+" users="+users);
        }

        if(param.getActivityName().contains(" ")){
            throw new ServerException(ServerConstantCode.ACTIVITY_CONTAINS_SPACE);
        }
    }

    private void saveToRedis(ActivityDto activity, List<ActivityPrizeInf> acInfS, List<UserDto> usInfS) {

        ActivityInf activityInf = new ActivityInf(activity, acInfS, usInfS);

        String encode = SerializeUtil.encode(activityInf);

        log.info("储存在redis中的值 {}", encode);

        redisUtil.set(CREATE_ACTIVITY + activity.getId(), encode);
    }

    private List<ActivityUser> saveActivityUser(ActivityDto activity, List<ActivityUser> activityUserList) {
        activityUserList.forEach(au -> {
            ActivityUserDto ad = new ActivityUserDto(null, activity.getId(), au.getUserId(), au.getUserName(), ActivityStatusEnum.USER_RUNNING);
            activityUserMapper.insert(ad);
        });
        return activityUserList;
    }

    private List<ActivityPrize> saveActivityPrize(ActivityDto activity, List<ActivityPrize> activityPrizeList) {
        activityPrizeList.forEach(ac -> {
            ActivityPrizeDto ad = new ActivityPrizeDto(null, activity.getId(), ac.getPrizeId(), ac.getPrizeAmount(), ac.getPrizeTiers(), ActivityStatusEnum.PRIZE_RUNNING);
            activityPrizeMapper.insert(ad);
        });
        return activityPrizeList;
    }

    private List<ActivityPrizeInf> convertToActivityPrizeInf(List<ActivityPrize> activityPrizeList, List<PrizeDO> prizeDOS) {

        //这里按prizeId 给这里俩个排个序
        activityPrizeList.sort(new ActivityPrizeListSort());

        prizeDOS.sort(new PrizeListSort());

        log.info("排好序的 活动奖品 {}  奖品的详细信息{}", activityPrizeList, prizeDOS);

        List<ActivityPrizeInf> acInf = new ArrayList<>();
        //整合prizeDOS activityPrizeList
        for (int i = 0; i < prizeDOS.size(); i++) {
            PrizeDO prizeDO = prizeDOS.get(i);

            ActivityPrize ac = activityPrizeList.get(i);

            ActivityPrizeInf inf = new ActivityPrizeInf(prizeDO.getId(), prizeDO.getName(),
                    prizeDO.getDescription(), prizeDO.getPrice(),
                    prizeDO.getImageUrl(), ac.getPrizeAmount(), ac.getPrizeTiers(), true);

            acInf.add(inf);
        }

        log.info("整合ActivityPrize和prize信息的结果 ActivityPrize：{}\nprize: {}\nacI:{}", activityPrizeList, prizeDOS, acInf);

        return acInf;
    }

    /**
     *  ret
     *  sum  activityId activityName valid(active,inactive)
     */
    @Override
    public ActivityListDto findActivityList() {
        //获取到活动的ids
        List<ActivityDO> activityDOS = activityMapper.selectActivity();

        if (activityDOS == null || activityDOS.isEmpty()) {
            log.info("没有活动");
            return null;
        }

        ActivityListDto dto = new ActivityListDto();
        dto.setTotal(activityDOS.size());

        //这里把值赋给active

        List<ActivityListDto.Active> collect = new ArrayList<>(activityDOS.stream().map(ac -> {
            return ActivityListDto.setActive(ac.getId(), ac.getActivityName(), ac.getDescription(),
                    ac.getStatus().equals(ActivityStatusEnum.RUNNING));
        }).toList());

        collect.sort(new sortActiveByStatus());

        dto.setRecords(collect);

        return dto;
    }


    @Override
    public List<WinningRecordDO> saveWinningInfo(DrawPrizesData param) {
        try {
            //差活动
            ActivityDto activity = activityMapper.selectActivityById(param.getActivityId());

            //这个奖品和活动的关联
            ActivityPrizeDO activityPrize = activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());

            //查奖品详细信息
            PrizeDO prize = prizeMapper.selectById(param.getPrizeId());

            //差人员信息
            List<Long> ids = param.getWinnerList().stream().map(DrawPrizesData.LuckUserList::getUserId).toList();

            List<UserDto> users = userMapper.selectByIds(ids);

            //返回中奖信息
            return saveInfo(activity, activityPrize, prize, users);

        }catch (Exception e) {
            log.info("查询信息出现了错误: message {}", e.getMessage(), e);
            throw e;
        }
    }

    private List<WinningRecordDO> saveInfo(ActivityDto activity, ActivityPrizeDO activityPrize, PrizeDO prize, List<UserDto> users) {

        List<WinningRecordDO> ret =  new ArrayList<>();

        for (UserDto u : users) {

            WinningRecordDO w = new WinningRecordDO(activity.getId(), activity.getActivityName(),
                    prize.getId(), prize.getName(), activityPrize.getPrizeTiers(),
                    u.getId(), u.getUserName(), u.getEmail(), u.getPhoneNumber(), new Date());
            try {

                log.info("储存中奖信息: {}", w);

                winningRecordMapper.insertWinningRecordMapper(w);

                ret.add(w);

            } catch (Exception e) {
                log.error("储存中奖信息出现了错误: message {}", e.getMessage(), e);
                throw e;
            }
        }
        return ret;
    }
}

class ActivityPrizeListSort implements Comparator<ActivityPrize> {

    @Override
    public int compare(ActivityPrize o1, ActivityPrize o2) {
        return Long.compare(o1.getPrizeId(), o2.getPrizeId());
    }

    @Override
    public boolean equals(Object obj) {
        return false;
    }
}

class PrizeListSort implements Comparator<PrizeDO> {

    @Override
    public int compare(PrizeDO o1, PrizeDO o2) {
        return Long.compare(o1.getId(), o2.getId());
    }

    @Override
    public boolean equals(Object obj) {
        return false;
    }
}
class sortActiveByStatus implements Comparator<ActivityListDto.Active> {

    @Override
    public int compare(ActivityListDto.Active o1, ActivityListDto.Active o2) {
        return o1.getValid() && !o2.getValid() ? -1 : 1;
    }
}